#include <stdint.h>
#include <memory.h>
#include <text_video.h>
#include <task.h>
#include <cpuid.h> 

static const char * L2_associativity[0x10] = 
{
    "disabled", "direct mapped",
    "2-way", "reserved",
    "4-way", "reserved",
    "8-way", "reserved",
    "16-way", "reserved", "reserved",
    "reserved", "reserved", "reserved", "reserved",
    "fully associative"
};

static void parse_cpuid_ext_2(void)
{
    uint32_t eax, ebx, ecx, edx;
    
    eax = 0x80000002;
    asm volatile("cpuid;":"=a"(eax),"=b"(ebx),"=c"(ecx),"=d"(edx):"a"(eax));
    
    *(uint32_t*)(cpuid.cpu_brand + 0) = (uint32_t)eax;
    *(uint32_t*)(cpuid.cpu_brand + 4) = (uint32_t)ebx;
    *(uint32_t*)(cpuid.cpu_brand + 8) = (uint32_t)ecx;
    *(uint32_t*)(cpuid.cpu_brand + 12) = (uint32_t)edx;
    
    if(cpuid.max_ext_input >= 0x80000003){
    eax = 0x80000003;
    asm volatile("cpuid;":"=a"(eax),"=b"(ebx),"=c"(ecx),"=d"(edx):"a"(eax));    

    *(uint32_t*)(cpuid.cpu_brand + 16) = (uint32_t)eax;
    *(uint32_t*)(cpuid.cpu_brand + 20) = (uint32_t)ebx;
    *(uint32_t*)(cpuid.cpu_brand + 24) = (uint32_t)ecx;
    *(uint32_t*)(cpuid.cpu_brand + 28) = (uint32_t)edx;}   
    
    if(cpuid.max_ext_input >= 0x80000004){
    eax = 0x80000004;
    asm volatile("cpuid;":"=a"(eax),"=b"(ebx),"=c"(ecx),"=d"(edx):"a"(eax));    

    *(uint32_t*)(cpuid.cpu_brand + 32) = (uint32_t)eax;
    *(uint32_t*)(cpuid.cpu_brand + 36) = (uint32_t)ebx;
    *(uint32_t*)(cpuid.cpu_brand + 40) = (uint32_t)ecx;
    *(uint32_t*)(cpuid.cpu_brand + 44) = (uint32_t)edx;} 
}

static void parse_cpuid_ext_6(void)
{
    uint32_t eax, ebx, ecx, edx;
    
    eax = 0x80000006;
    asm volatile("cpuid;":"=a"(eax),"=b"(ebx),"=c"(ecx),"=d"(edx):"a"(eax));
    
    cpuid.l2_assoc = (uint8_t)((ecx >> 12) & 0xF);
    cpuid.l2_cache_size = (uint16_t)((ecx >> 16) & 0xFFFF);
    cpuid.l2_cache_line_size = (uint8_t)(ecx & 0xFF);
}

static void parse_cpuid_ext_0(void)
{
    uint32_t eax;
    
    eax = 0x80000000;
    asm volatile("cpuid;":"=a"(eax):"a"(eax));
    
    cpuid.max_ext_input = (uint32_t)eax;
}

static void parse_cpuid_basic_4(void)
{
    uint32_t eax, ebx, ecx, edx;
    uint8_t i;
   
    cpuid.cache_count = 0;
   
    for(i = 0; i < 32; i++)
    {
        eax = 4;
        ecx = i;
        asm volatile("cpuid;":"=a"(eax),"=b"(ebx),"=c"(ecx),"=d"(edx):"a"(eax));
        
        if(!(eax & 0x0F)){break;}
        
        cpuid.cache_info->type = (uint8_t)((eax >> 0) & 0x0F);
        cpuid.cache_info->level = (uint8_t)((eax >> 5) & 0x03);
        
        cpuid.cache_info->ways = (uint8_t)((ebx >> 22) & 0xFF);
        cpuid.cache_info->partitions = (uint8_t)((ebx >> 12) & 0xFF);
        cpuid.cache_info->line_size = (uint16_t)((ebx >> 0) & 0x3FF);
        cpuid.cache_info->sets = ecx;
        
        cpuid.cache_info->cache_size = (uint32_t)((uint32_t)(cpuid.cache_info->ways + 1) * 
                                                  (uint32_t)(cpuid.cache_info->partitions + 1) * 
                                                  (uint32_t)(cpuid.cache_info->line_size + 1) * 
                                                  (uint32_t)(cpuid.cache_info->sets + 1));
        cpuid.cache_count++;                                      
    }
}

static void parse_cpuid_basic_2(void)
{
    uint32_t eax, ebx, ecx, edx;
    uint8_t i;
    uint32_t reg;
    uint8_t tmp;
        
    eax = 2;
    asm volatile("cpuid;":"=a"(eax),"=b"(ebx),"=c"(ecx),"=d"(edx):"a"(eax));

}

static void parse_cpuid_basic_1(void)
{
    uint32_t eax, ebx, ecx, edx;
    
    eax = 1;
    asm volatile("cpuid;":"=a"(eax),"=b"(ebx),"=c"(ecx),"=d"(edx):"a"(eax));
    
    cpuid.features_ecx = ecx;
    cpuid.features_edx = edx;
    
    cpuid.stepping = (uint8_t)(eax & 0xF);
    cpuid.model = (uint8_t)((eax >> 4) & 0xF);
    cpuid.family = (uint8_t)((eax >> 8) & 0xF);
    cpuid.type = (uint8_t)((eax >> 12) & 0x3);
    cpuid.ext_model = (uint8_t)((eax >> 16) & 0xF);
    cpuid.ext_family = (uint8_t)((eax >> 20) & 0xFF);
    
    cpuid.cache_line_size = (uint16_t)(((ebx >> 8) & 0xFF) * 8);
}

static void parse_cpuid_basic_0(void)
{
    uint32_t eax, ebx, ecx, edx;
    
    eax = 0;
    asm volatile("cpuid;":"=a"(eax),"=b"(ebx),"=c"(ecx),"=d"(edx):"a"(eax));

    clear_memory(cpuid.cpu_signature, 13);
    
    cpuid.max_basic_input = (uint32_t)eax;
    *(uint32_t*)(cpuid.cpu_signature + 0) = (uint32_t)ebx;
    *(uint32_t*)(cpuid.cpu_signature + 4) = (uint32_t)edx;
    *(uint32_t*)(cpuid.cpu_signature + 8) = (uint32_t)ecx;
}

void parse_cpuid(void)
{
    clear_memory(cpuid.cpu_brand, 49);
    
    parse_cpuid_basic_0();
    if(cpuid.max_basic_input >= 0x01){parse_cpuid_basic_1();}
    if(cpuid.max_basic_input >= 0x02){parse_cpuid_basic_2();}
    if(cpuid.max_basic_input >= 0x04){parse_cpuid_basic_4();}

    parse_cpuid_ext_0();
    if(cpuid.max_ext_input >= 0x80000002){parse_cpuid_ext_2();}
    if(cpuid.max_ext_input >= 0x80000006){parse_cpuid_ext_6();}
}
